#pragma once
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
//#include <mpi.h>
#include <stdio.h>
#include <limits>

#include "grid_struct.hpp"

#define my_min(a, b) (((a) < (b)) ? (a) : (b))

template <typename V>
int Serial_read_FEM_sourcegrid(const std::string &filename, std::vector<FEM_struct<V>> &gridDataArray, V axisMax[3], V axisMin[3])
{
    std::ifstream file(filename);
    if (!file.is_open())
    {
        std::cerr << "Fail to Open the File: " << filename << std::endl;
        return 1;
    }

    const size_t bufferSize = 1 << 20; // 1 MB buffer
    char *buffer = new char[bufferSize];
    file.rdbuf()->pubsetbuf(buffer, bufferSize);
        
    size_t lineCount = 0;
    std::string line;
    while (std::getline(file, line))
    {
        ++lineCount;
    }
    file.clear();
    file.seekg(0, std::ios::beg);

    lineCount -= 1;

    gridDataArray.resize(lineCount);

    std::cout << "File : " << filename << " have " << lineCount << " lines data." << std::endl;

    std::getline(file, line);
    
    axisMin[0] = axisMin[1] = axisMin[2] = std::numeric_limits<V>::max();
    axisMax[0] = axisMax[1] = axisMax[2] = std::numeric_limits<V>::lowest();

    // #pragma omp for nowait
    for (size_t i = 0; i < lineCount; ++i)
    {
        std::getline(file, line);
        char *str = const_cast<char*>(line.c_str());

        V x, y, z, s1;
        x = std::strtod(str, &str);
        y = std::strtod(str, &str);
        z = std::strtod(str, &str);
        s1 = std::strtod(str, &str);

        FEM_struct<V> data = {x, y, z, s1};
        gridDataArray[i] = data;

        axisMin[0] = std::min(axisMin[0], x);
        axisMax[0] = std::max(axisMax[0], x);
        axisMin[1] = std::min(axisMin[1], y);
        axisMax[1] = std::max(axisMax[1], y);
        axisMin[2] = std::min(axisMin[2], z);
        axisMax[2] = std::max(axisMax[2], z);
    }

    delete[] buffer;
    file.close();

    return 0;
}

template <typename V>
int Serial_read_FEM_destgrid(const std::string &filename, std::vector<FEM_struct<V>> &gridDataArray)
{
    std::ifstream file(filename);
    if (!file.is_open())
    {
        std::cerr << "Fail to Open the File: " << filename << std::endl;
        return 1;
    }

    const size_t bufferSize = 1 << 20; // 1 MB buffer
    char *buffer = new char[bufferSize];
    file.rdbuf()->pubsetbuf(buffer, bufferSize);

    size_t lineCount = 0;
    std::string line;
    while (std::getline(file, line))
    {
        ++lineCount;
    }
    file.clear();
    file.seekg(0, std::ios::beg);

    lineCount -= 1;

    gridDataArray.resize(lineCount);

    std::cout << "File : " << filename << " have " << lineCount << " lines data." << std::endl;

    std::getline(file, line);


        // #pragma omp for nowait
    for (size_t i = 0; i < lineCount; ++i)
    {
        std::getline(file, line);
        char *str = const_cast<char*>(line.c_str());

        int No;
        V x, y, z;
        No = std::strtol(str, &str, 10);
        x = std::strtod(str, &str);
        y = std::strtod(str, &str);
        z = std::strtod(str, &str);

        FEM_struct<V> data = {x, y, z, 0.0};
        gridDataArray[i] = data;
    }

    delete[] buffer;
    file.close();

    return 0;
}

template <typename V>
int Serial_store_FEM_destgrid(const std::string &filename, std::vector<FEM_struct<V>> &gridDataArray)
{
    FILE *file = fopen(filename.c_str(), "w");
    //FILE* file = nullptr;
    //errno_t err = fopen_s(&file, filename.c_str(), "w");
    if (!file) {
        fprintf(stderr, "Fail to Open the File: %s\n", filename.c_str());
        return -1;
    }

    fprintf(file, "x\ty\tz\tS1\n");

    for (size_t i = 0; i < gridDataArray.size(); ++i) {
        fprintf(file, "%.0f\t%.0f\t%.0f\t%.8f\n",
                gridDataArray[i].x,
                gridDataArray[i].y,
                gridDataArray[i].z,
                gridDataArray[i].s1);
    }
    fclose(file);

    return 0;
}

